/*
 * @Author: xiaosihan
 * @Date: 2021-05-09 09:31:44
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-10-22 22:58:43
 */

import _ from "lodash";
import * as THREE from "three";
import { BufferGeometry, InstancedBufferAttribute, Material } from "three";

export default class THREEInstancedMesh extends THREE.InstancedMesh {

    constructor(geometry: BufferGeometry, material: Material) {
        super(geometry, material, 1000);
        this.count = 0;

        this.instanceColor = new InstancedBufferAttribute(new Float32Array(3000).fill(1), 3);
    }

    isTHREEInstancedMesh = true;

    instanceChrildren: Array<THREEInstancedMeshItem> = [];

    addItem(item: THREEInstancedMeshItem) {
        this.count++;

        item.instancedMeshParent = this;

        this.instanceChrildren.push(item);

        let index = this.instanceChrildren.indexOf(item);

        this.setMatrixAt(index, item.matrixWorld);

        return this;
    }

    removeItem(item: THREEInstancedMeshItem) {

        item.instancedMeshParent = undefined;

        let index = this.instanceChrildren.indexOf(item);

        if (index > -1) {
            this.instanceChrildren.splice(index, 1);
            this.count--;
        }

        return this;
    }

    setMatrixAtByItem(item: THREEInstancedMeshItem) {

        let index = this.instanceChrildren.indexOf(item);

        if (index > -1) {
            this.setMatrixAt(index, item.matrixWorld);
            this.instanceMatrix.needsUpdate = true;
        }
        return this;
    }

    setColorAtByItem(item: THREEInstancedMeshItem, color: THREE.Color) {
        let index = this.instanceChrildren.indexOf(item);

        if (index > -1) {
            this.setColorAt(index, color);
            this.instanceColor!.needsUpdate = true;
        }
        return this;
    }
}

export class THREEInstancedMeshItem extends THREE.Mesh {
    constructor() {
        super();
    }

    isTHREEInstancedMeshItem = true;

    instancedMeshParent?: THREEInstancedMesh;

    color = new THREE.Color();

    updateMatrixWorld(force: boolean) {

        if (_.get(this, ["parent", "isTHREEInstancedMesh"])) {
            this.matrixWorld.copy(this.matrix);
            (this.parent as THREEInstancedMesh).setMatrixAtByItem(this);

        } else if (this.parent) {
            this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
        }

        if (this.instancedMeshParent) {
            this.instancedMeshParent.setMatrixAtByItem(this);
        }

        super.updateMatrixWorld(force);
    }

}